import { CodeGroup, ContentByFramework, ExpoLogo, ReactLogo, RNLogo, SvelteLogo, TabbedCodeGroup, TabbedCodeGroupItem, VanillaLogo } from "@/components/forMdx";

export const metadata = {
  description: "ImageDefinition is a CoValue for storing images with built-in UX features."
};

# ImageDefinition

`ImageDefinition` is a specialized CoValue designed specifically for managing images in Jazz applications. It extends beyond basic file storage by supporting a blurry placeholder, built-in resizing, and progressive loading patterns.

Beyond `ImageDefinition`, Jazz offers higher-level functions and components that make it easier to use images:
- [`createImage()`](#creating-images) - function to create an `ImageDefinition` from a file
- [`loadImage`, `loadImageBySize`, `highestResAvailable`](#displaying-images) - functions to load and display images
<ContentByFramework framework={['react', 'svelte','react-native','react-native-expo']}>
- [`Image`](#displaying-images) - Component to display an image
</ContentByFramework>

<ContentByFramework framework="react">
The [Image Upload example](https://github.com/gardencmp/jazz/tree/main/examples/image-upload) demonstrates use of images in Jazz.
</ContentByFramework>

<ContentByFramework framework="svelte">
The [Chat example](https://github.com/gardencmp/jazz/tree/main/examples/chat-svelte) includes an implementation of ImageDefinitions in Svelte.
</ContentByFramework>

<ContentByFramework framework={["react-native"]}>
## Installation [!framework=react-native]
Jazz's images implementation is based on `@bam.tech/react-native-image-resizer`. Check the [installation guide](/docs/react-native/project-setup#install-dependencies) for more details.
</ContentByFramework>

<ContentByFramework framework={["react-native-expo"]}>
## Installation [!framework=react-native-exp]
Jazz's images implementation is based on `expo-image-manipulator`. Check the [installation guide](/docs/react-native-expo/project-setup#install-dependencies) for more details.
</ContentByFramework>

## Creating Images

The easiest way to create and use images in your Jazz application is with the `createImage()` function:

<TabbedCodeGroup id="create-image" default="react" savedPreferenceKey="framework">
<TabbedCodeGroupItem label="Vanilla" icon={<VanillaLogo />} value="vanilla" preferWrap>
```ts index.ts#Basic
```
</TabbedCodeGroupItem>
<TabbedCodeGroupItem label="React" icon={<ReactLogo />} value="react" preferWrap>
```ts index.ts#Basic
```
</TabbedCodeGroupItem>
<TabbedCodeGroupItem label="Svelte" icon={<SvelteLogo />} value="svelte" preferWrap>
```ts index.ts#Basic
```
</TabbedCodeGroupItem>
<TabbedCodeGroupItem label="React Native" icon={<RNLogo />} value="react-native" preferWrap>
```ts rn.tsx#Basic
```
</TabbedCodeGroupItem>
<TabbedCodeGroupItem label="React Native (Expo)" icon={<ExpoLogo />} value="react-native-expo" preferWrap>
```ts rn.tsx#Basic
```
</TabbedCodeGroupItem>
</TabbedCodeGroup>

The `createImage()` function:
- Creates an `ImageDefinition` with the right properties
- Optionally generates a small placeholder for immediate display
- Creates multiple resolution variants of your image
- Returns the created `ImageDefinition`

### Configuration Options

<CodeGroup>
```ts declarations.ts#CreateImageDeclaration
```
</CodeGroup>

#### `image`

The image to create an `ImageDefinition` from.
<ContentByFramework framework={['react', 'svelte', 'vanilla']}>
 This can be a `Blob` or a `File`.
</ContentByFramework>
<ContentByFramework framework={['react-native', 'react-native-expo']}>
  This must be a `string` with the file path.
</ContentByFramework>

#### `owner`

The owner of the `ImageDefinition`. This is used to control access to the image. See [Groups as permission scopes](/docs/permissions-and-sharing/overview) for more information on how to use groups to control access to images.

#### `placeholder`

Disabled by default. This option allows you to automatically generate a low resolution preview for use while the image is loading. Currently, only `"blur"` is a supported.

#### `maxSize`

The image generation process includes a maximum size setting that controls the longest side of the image. A built-in resizing feature is applied based on this setting.

#### `progressive`

The progressive loading pattern is a technique that allows images to load incrementally, starting with a small version and gradually replacing it with a larger version as it becomes available. This is useful for improving the user experience by showing a placeholder while the image is loading.

Passing `progressive: true` to `createImage()` will create internal smaller versions of the image for future uses.

### Create multiple resized copies

To create multiple resized copies of an original image for better layout control, you can use the `createImage` function multiple times with different parameters for each desired size. Here’s an example of how you might implement this:

<CodeGroup>
```ts index.ts#CreateResized
```
</CodeGroup>

### Creating images on the server

We provide a `createImage` function to create images from server side using the same options as the browser version, using the package `jazz-tools/media/server`. Check the [server worker](/docs/server-side/setup) documentation to learn more.

The resize features are based on the `sharp` library, then it is requested as peer dependency in order to use it.

<TabbedCodeGroup id="install-sharp" default="pnpm" savedPreferenceKey="package-manager">
  <TabbedCodeGroupItem label="npm">
```sh
npm install sharp
```
  </TabbedCodeGroupItem>
  <TabbedCodeGroupItem label="pnpm">
```sh
pnpm install sharp
```
  </TabbedCodeGroupItem>
</TabbedCodeGroup>

<CodeGroup className="mt-4">
```ts server.ts
```
</CodeGroup>

## Displaying Images

<ContentByFramework framework={['react', 'svelte', 'react-native', 'react-native-expo']}>
To use the stored ImageDefinition, there are two ways: declaratively, using the `Image` component, and imperatively, using the static methods.
</ContentByFramework>

<ContentByFramework framework={['react', 'svelte', 'react-native', 'react-native-expo']}>
  The Image component is the best way to let Jazz handle the image loading.
### `<Image>` component [!framework=react,svelte,react-native,react-native-expo]
<TabbedCodeGroup id="image-component" default="react" savedPreferenceKey="framework">
<TabbedCodeGroupItem label="React" icon={<ReactLogo />} value="react" preferWrap>
```tsx react-snippet.tsx#GalleryView
```
</TabbedCodeGroupItem>
<TabbedCodeGroupItem label="Svelte" icon={<SvelteLogo />} value="svelte" preferWrap>
```svelte GalleryView.svelte
```
</TabbedCodeGroupItem>
<TabbedCodeGroupItem label="React Native" icon={<RNLogo />} value="react-native" preferWrap>
```tsx GalleryViewRN.tsx#GalleryView
```
</TabbedCodeGroupItem>
<TabbedCodeGroupItem label="React Native (Expo)" icon={<ExpoLogo />} value="react-native-expo" preferWrap>
```tsx GalleryViewExpo.tsx#GalleryView
```
</TabbedCodeGroupItem>
</TabbedCodeGroup>

The `Image` component handles:
- Showing a placeholder while loading, if generated or specified
- Automatically selecting the appropriate resolution, if generated with progressive loading
- Progressive enhancement as higher resolutions become available, if generated with progressive loading
- Determining the correct width/height attributes to avoid layout shifting
- Cleaning up resources when unmounted

The component's props are:

</ContentByFramework>
<ContentByFramework framework={["react", "svelte"]}>
<CodeGroup>
```ts declarations.ts#BrowserImageProps
```
</CodeGroup>
</ContentByFramework>
<ContentByFramework framework={['react-native', 'react-native-expo']}>
<CodeGroup>
  ```ts declarations.ts#RNImageProps
```
</CodeGroup>
</ContentByFramework>

<ContentByFramework framework={['react','svelte','react-native','react-native-expo']}>
#### Width and Height props [!framework=react,svelte,react-native,react-native-expo]

The `width` and `height` props are used to control the best resolution to use but also the width and height attributes of the image tag.

Let's say we have an image with a width of 1920px and a height of 1080px.

<CodeGroup className="[&_span]:whitespace-normal">
```tsx react-snippet.tsx#MultiImages
```
</CodeGroup>

If the image was generated with progressive loading, the `width` and `height` props will determine the best resolution to use.

</ContentByFramework>
<ContentByFramework framework={['react', 'svelte']}>
#### Lazy loading [!framework=react,svelte]

The `Image` component supports lazy loading with the [same options as the native browser `loading` attribute].(https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/img#loading). It will generate the blob url for the image when the browser's viewport reaches the image.

<CodeGroup className="[&_span]:whitespace-normal">
```tsx react-snippet.tsx#LazyLoad
```
</CodeGroup>
</ContentByFramework>

<ContentByFramework framework={['react', 'svelte','react-native','react-native-expo']}>

#### Placeholder

You can choose to specify a custom placeholder to display as a fallback while an image is loading in case your image does not have a placeholder generated. A data URL or a URL for a static asset works well here.

### Imperative Usage [!framework=react,svelte,react-native,react-native-expo]
</ContentByFramework>

Like other CoValues, `ImageDefinition` can be used to load the object.

<CodeGroup>
```tsx index.ts#Imperative
```
</CodeGroup>

`image.original` is a `FileStream` and its content can be read as described in the [FileStream](/docs/core-concepts/covalues/filestreams#reading-from-filestreams) documentation.

Since FileStream objects are also CoValues, they must be loaded before use. To simplify loading, if you want to load the binary data saved as Original, you can use the `loadImage` function.

<CodeGroup>
```tsx index.ts#LoadImageHelper
```
</CodeGroup>

If the image was generated with progressive loading, and you want to access the best-fit resolution, use `loadImageBySize`. It will load the image of the best resolution that fits the wanted width and height.

<CodeGroup>
```tsx index.ts#LoadImageBySize
```
</CodeGroup>


If want to dynamically listen to the _loaded_ resolution that best fits the wanted width and height, you can use the `subscribe` and the `highestResAvailable` function.

<CodeGroup>
```tsx index.ts#HighestResAvailable
```
</CodeGroup>

## Custom image manipulation implementations

To manipulate images (like placeholders, resizing, etc.), `createImage()` uses different implementations depending on the environment.

<ContentByFramework framework={['react', 'svelte']}>
On the browser, image manipulation is done using the `canvas` API.
</ContentByFramework>
<ContentByFramework framework="react-native">
On React Native, image manipulation is done using the `@bam.tech/react-native-image-resizer` library.
</ContentByFramework>
<ContentByFramework framework="react-native-expo">
On Expo, image manipulation is done using the `expo-image-manipulator` library.
</ContentByFramework>

If you want to use a custom implementation, you can use the `createImageFactory` function in order create your own `createImage` function and use your preferred image manipulation library.

<CodeGroup>
```tsx index.ts#CreateImageFactory
```
</CodeGroup>

## Best Practices

- **Set image sizes** when possible to avoid layout shifts
- **Use placeholders** (like LQIP - Low Quality Image Placeholders) for instant rendering
- **Prioritize loading** the resolution appropriate for the current viewport
- **Consider device pixel ratio** (window.devicePixelRatio) for high-DPI displays
- **Always call URL.revokeObjectURL** after the image loads to prevent memory leaks
